home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / cppl1a.zip / TEXT.OUT < prev   
Text File  |  1990-07-15  |  27KB  |  812 lines

  1. typesetting notes:  (8U(s1S...(s0S surrounds Italic text, and (8U(s3B...(s0B
  2. is for bold face.  Due to the heavy use of programming keywords
  3. in the text, it would be impossible to read without some
  4. representation for the alternate font.  You can use a search and
  5. replace to convert these to your preferred format (like ` ' or [ ]
  6. for reading online) or the printer control codes you need or the
  7. correct commands to import into your word processor.
  8.  
  9. This is the first C++ lesson.  The subject is Operators.  This
  10. is adapted from material in my upcoming book.  Please direct any
  11. comments and criticsm to me on the forum or by email.
  12.  
  13. --John
  14.  
  15.  
  16. (8U(s3BC++ Operators(s0B
  17. Copyright 1990 by John M. Dlugosz
  18.  
  19.  
  20. Say you have defined a (8U(s1Scomplex(s0S type to work with complex numbers.
  21. In C, to add two complex numbers you would have to have a
  22. function like (8U(s1Scomplex add(complex,complex)(s0S.  An expression of any
  23. size would end up looking more like LISP than C.  In C++, you can
  24. overload operators.  In this case, I would much rather say (8U(s1Sc=a+b;(s0S
  25. than (8U(s1Sc=add(a,b)(s0S.  Naturally, I can do exactly that.
  26.  
  27. The name of an operator is the reserved word (8U(s1Soperator(s0S followed by
  28. the symbol of the operator being referred to.  In this case,
  29. (8U(s1Soperator+(s0S is what I am after.  (8U(s1Soperator+(s0S can be treated just like
  30. any other function name.  That is how I define the function:
  31. (8U(s1Scomplex operator+ (complex,complex);(s0S  It is exactly like (8U(s1Sadd()(s0S
  32. except the name of the function is now (8U(s1Soperator+(s0S.  I can use it
  33. just like I did above, as (8U(s1Sc=operator+(a,b);(s0S  That is hardly an
  34. improvement!  But I can also use the operator in its natural
  35. operator syntax, as (8U(s1Sc=a+b;(s0S
  36.  
  37. The operators are just like functions with a special name, but
  38. some restrictions apply.  You can only define those operators
  39. that exist, with the correct number of parameters.  The type of
  40. the parameters are much more flexible, and is the whole point of
  41. defining them.  However, at least one parameter to an operator
  42. function must be of a class type.  So, you could not define (8U(s1Svoid
  43. operator+ (char*, char*);) because all arguments are of built-in
  44. types.
  45.  
  46. Operators can be member functions.  In this case, the first
  47. argument is the receiver and the function is declared with one
  48. less argument than it actually has.  So if I defined operator+ as
  49. a member, I would have  (8U(s1Scomplex complex::operator+ (complex);(s0S  It
  50. can be used as (8U(s1Sc=a.operator+(b);(s0S as a member function with a
  51. funny name, or as the normal infix operator.  In the infix
  52. syntax, the left argument is taken as the object being operated
  53. on.
  54.  
  55. (8U(s3Bavailable operators(s0B
  56.  
  57. These are the operators that can be overloaded:
  58.  
  59. new    delete    (8U(s1Sstorage allocation(s0S
  60. +    -    *    (8U(s1Sboth unary and binary forms
  61. &    (8U(s1Sunary and binary, unary is special(s0S
  62. =    (8U(s1Sassignment operator is special(s0S
  63. /    %    ^    |    ~    !    <    >    <<    >>    ==
  64. !=    +=    -=    *=    /=    %=    ^=    &=    |=    ->*
  65. &&    ||    ,    (8U(s1Shave left to right evaluation(s0S
  66. ++    --    (8U(s1Sprefix and postfix, special way to distinguish
  67. ->    ()    []    (8U(s1Sspecial in various ways(s0S
  68.  
  69. In addition, (8U(s1Sconversion operators(s0S are also defined with the
  70. operator keyword.  Conversion operators are covered in a later
  71. chapter.
  72.  
  73. Some operators can be used in more than one way.  For example,
  74. operator- can be used as unary (one argument) or binary (two
  75. arguments).  All operators keep the same order of precedence when
  76. they are redefined.
  77.  
  78. (8U(s3Bdefining an operator(s0B
  79.  
  80. Back to the example of complex addition.  Here is a (8U(s1Scomplex(s0S class
  81. that has operator+= and operator+ defined for complex numbers.
  82.  
  83. (8U&a6LLISTING
  84. //example using overloaded operators to define arithmetic
  85. //on complex numbers.
  86.  
  87. class complex {
  88.    double x, y;
  89. public:
  90.    complex (double xx, double yy) { x=xx; y=yy; }
  91.    complex& operator+= (const complex& b);
  92.    friend complex operator+ (const complex& a, const complex& b);
  93.    };
  94.  
  95. complex& complex::operator+= (const complex& b)
  96. {
  97. x += b.x;
  98. y += b.y;
  99. return *this;
  100. }
  101.  
  102. complex operator+ (const complex& a, const complex& b)
  103. {
  104. complex temp= a;
  105. return temp += b;
  106. }
  107. (8U&a1LLISTING
  108.  
  109. The operator+= is defined as a member function.  It takes one
  110. argument in addition to the implicit (8U(s1Sthis(s0S.  The line (8U(s1Sreturn temp
  111. += b;) is equivalent to (8U(s1Sreturn temp.operator+=(b);(s0S.  Remember,
  112. operators defined as members have one less argument defined than
  113. they actually take.
  114.  
  115. (8U(s3Bstandard input and output(s0B
  116.  
  117. The (8U(s1Sstream(s0S library in C++ uses overloaded operators for standard
  118. input and output.  The operator<< is used as a "put to", and
  119. operator>> is used as a "get from".  There are classes (8U(s1Sostream(s0S
  120. and (8U(s1Sistream(s0S to handle output and input.  Functions such as:
  121.  
  122. (8U&a6LLISTING
  123. ostream& operator<< (ostream&, int);
  124. ostream& operator<< (ostream&, const char*);
  125. istream& operator>> (istream&, int&);
  126. istream& operator>> (istream&, char*&);
  127. (8U&a1LLISTING
  128.  
  129. Are used to handle input and output.  Each operator returns the
  130. first argument as the function result, so they can be chained
  131. together.  The standard input and output are available as
  132. variables (8U(s1Scin(s0S and (8U(s1Scout(s0S.
  133.  
  134. (8U&a6LLISTING
  135. cout << "hello world!\n";
  136. cout << "the answer is:" << x;
  137. cin >> x >> y;  //read values into x and y;
  138. (8U&a1LLISTING
  139.  
  140. You can define your own output and input operators to operate on
  141. your own types.
  142.  
  143. (8U&a6LLISTING
  144. ostream& operator<< (ostream& o, const complex& c)
  145. {
  146. //I'm a friend, and have access to c.x and c.y
  147. o << '(' << c.x << ',' << c.y << ')';
  148. return o;
  149. }
  150.  
  151. istream& operator>> (istream& i, complex& c)
  152. {
  153. double x, y;
  154. char c;
  155. i >> c >> x >> c >> y >> c;
  156. c= complex(x,y);
  157. return o;
  158. }
  159. (8U&a1LLISTING
  160.  
  161. The input operator is a little overkill for such a simple
  162. structure-- you could have made it a friend and just read in the
  163. x and y components directly.  But this illustrates a more
  164. general technique.  You read the data needed to create an object,
  165. and then call a constructor to put it together.
  166.  
  167. In general, use (8U(s1Scin >> x;(s0S to read a value, and (8U(s1Scout << x;(s0S to
  168. write a value.  That is all you have to remember until you get to
  169. the chapter on streams.
  170.  
  171. (8U(s3Boperator=(s0B
  172.  
  173. Normally when you write an assignment such as (8U(s1Sx=y;(s0S the value of y
  174. is moved to x simply by copying the bits.  The assignment
  175. operator lets you define how assignment will take place instead.
  176. Consider a class that contains pointers.  When you assign one
  177. instance to another, what you really want is a "deep copy" where
  178. the pointers are also duplicated to give the new copy its own.
  179.  
  180. (8U&a6LLISTING
  181. class C {
  182.    char* name;
  183.    int age;
  184. public:
  185.    // other members omitted for example...
  186.    C& operator= (C&);  //assignment operator
  187.    };
  188.  
  189. C& C::operator= (C& x)
  190. {
  191. age= x.age;  //copy each element
  192. free (name);  //free `name' before overwriting it!
  193. name= strdup (x.name);  //make unique copy
  194. return *this;
  195. }
  196. (8U&a1LLISTING
  197.  
  198. The assignment operator must be a member.  It should be used to
  199. provide assignment, and not for anything else.  The parameter can
  200. be any type though.  In fact, you can have multiple assignment
  201. operators defined for a class, so you can assign various things
  202. to it.
  203.  
  204. (8U&a6LLISTING
  205. class String {
  206.    char* s;
  207.    int len;
  208. public:
  209.    String& operator= (String&);
  210.    String& operator= (const char*);
  211.    String& operator= (char);
  212.    };
  213. (8U&a1LLISTING
  214.  
  215. In class C, notice that the operator= returns a (8U(s1SC&(s0S and in fact
  216. returns (8U(s1S*this(s0S.  This is common practice, and allows chaining of
  217. assignment and the use of the assignment in a larger expression
  218. as with the built-in use.  The built-in assignment operator
  219. returns the first argument as its result, so you can do things
  220. like (8U(s1Sfoo(a=b+1);(s0S and (8U(s1Sa=b=c;(s0S.  It is common practice to continue
  221. this tradition by returning (8U(s1S*this(s0S from operator=.
  222.  
  223. Assignment in C does not return an lvalue, but my supplied
  224. operator= does.  In C++, this has been extended to the built-in
  225. definitions as well.  This means that you could say something
  226. like  (8U(s1Sp= &(c=b);(s0S which takes the address of the return value from
  227. assignment, even for built-in types.  You could not do that in C:
  228. you would get the error (8U(s1Sargument to `&' must be an lvalue(s0S.
  229.  
  230. You can define operator= to return anything you like.  Generally
  231. it returns (8U(s1S*this(s0S or has no return value (defined as void), but
  232. there may be reasons to do otherwise.
  233.  
  234. The operator= is unique in that it is not inherited.  Actually,
  235. it is inherited in a special way.  If you have a class that has
  236. members or base classes that have an operator= defined, and do
  237. not provide an operator= in this class, the compiler generates
  238. one for you that calls operator= for those pieces of the class
  239. that have an operator= defined, and copies the rest.
  240.  
  241. (8U&a6LLISTING
  242. // example of derived class needing operator=
  243.  
  244. class D {
  245.    int x;
  246.    C y;  // C from above example
  247. public:
  248.    D& operator= (D&);
  249.    };
  250.  
  251. D& D::operator= (D& second)
  252. {
  253. x= second.x;  //normal copy
  254. y= second.y;  //calls C::operator=()
  255. return *this;
  256. }
  257. (8U&a1LLISTING
  258.  
  259. Since class D contained a member that should not be copied in the
  260. plain way, it defined an operator= that copied it correctly by
  261. calling the operator= on that member.  However, if I had not
  262. written (8U(s1SD::operator=()(s0S then it would still have done the same
  263. thing!  This is sometimes called the Miranda rule:  if you do not
  264. write an operator=, one will be provided for you.  Beware though that
  265. if you do write an operator= for a class, make sure you indeed
  266. copy everything in it.
  267.  
  268. (8U(s3BVersion Notes(s0B
  269.  
  270. In C++ versions prior to 2.0, there was no Miranda rule.  The
  271. operator= was not inherited at all.  If you did not provide a new
  272. operator= in a derived class, or did not write an operator= in a
  273. class with members that need it, you get a bit-for-bit copy
  274. anyway.
  275.  
  276. (8U(s3Boperator[](s0B
  277.  
  278. The operator[] can be overloaded, and it is one of the special
  279. ones.  First of all, the syntax is different.  Writing (8U(s1Sx[y];(s0S will
  280. call (8U(s1Sx.operator[](y);(s0S  Second, it must be a member function.
  281.  
  282. Here is an example of a vector class that uses this operator.
  283. This implements an array that grows as needed to accommodate new
  284. elements.
  285.  
  286. (8U&a6LLISTING
  287. #include <iostream.h>
  288.  
  289. typedef int eltype;
  290.  
  291. class vector {
  292.    eltype* elements;
  293.    int capacity;
  294. public:
  295.    vector (int startsize= 10);
  296.    ~vector() { delete[capacity] elements; }
  297.    int size() { return capacity; }
  298.    eltype& operator[] (int index);
  299.    };
  300.  
  301. vector::vector (int startsize)
  302. {
  303. capacity= startsize;
  304. elements= new eltype[startsize];
  305. }
  306.  
  307. eltype& vector::operator[] (int index)
  308. {
  309. if (index >= capacity) {  //out of bounds
  310.    eltype* new_array= new eltype[index+1];
  311.    for (int loop= 0;  loop < index;  loop++)
  312.       new_array[loop]= elements[loop];
  313.    delete[capacity] elements;
  314.    elements= new_array;
  315.    capacity= index+1;
  316.    }
  317. return elements[index];
  318. }
  319.  
  320. main()
  321. {
  322. vector a;
  323. for (;;) {
  324.    char c;
  325.    int index, value;
  326.    cout << "operation (r,w,q)? ";
  327.    cin >> c;
  328.    switch (c) {
  329.       case 'q':  goto done;
  330.       case 'r': //read test
  331.          cout << "  index ";
  332.          cin >> index;
  333.          if (index < 0 || index >= a.size())
  334.             cout << "index out of range.";
  335.          else cout << "contains " << a[index];
  336.          break;
  337.       case 'w': //write test
  338.          cout << "  index and value ";
  339.          cin >> index >> value;
  340.          a[index]= value;
  341.          break;
  342.       case 's': //size?
  343.          cout << "array holds " << a.size() << " elements.";
  344.          break;
  345.       case 'l': //list them
  346.          for (index= 0;  index < a.size();  index++)
  347.             cout << "\n [" << index << "] " << a[index];
  348.          break;
  349.       }
  350.    cout << '\n';
  351.    }
  352. done:
  353. cout << "program finished.\n";
  354. }
  355. (8U&a1LLISTING
  356.  
  357. The operator[] returns an element of the array, and returns it by
  358. reference.  This allows it to be used on the left hand side of an
  359. assignment, as seen in the write test in the main program.
  360.  
  361. This is a typical application-- to access a collection of
  362. elements.  The parameter to operator[] can be of any type though.
  363. For example, an associative array could associate strings and
  364. numbers.  Index it with a number and it returns the string, and
  365. index it with a string and it returns the number!  Interesting?
  366. Here is the code:
  367.  
  368. (8U&a6LLISTING
  369. #include <iostream.h>
  370. #include <string.h>
  371.  
  372. /* associative array of strings */
  373.  
  374. typedef char* eltype;
  375.  
  376. class asa {  //associative string array
  377.    eltype* elements;
  378.    int capacity;
  379. public:
  380.    asa (int startsize= 10);
  381.    ~asa() { delete[capacity] elements; }
  382.    int size() { return capacity; }
  383.    eltype& operator[] (int index);
  384.    int operator[] (eltype s);  //the 'backwords' version
  385.    };
  386.  
  387. asa::asa (int startsize)
  388. {
  389. capacity= startsize;
  390. elements= new eltype[startsize];
  391. }
  392.  
  393. int asa::operator[] (eltype s)
  394. {
  395. for (int loop= 0;  loop < capacity;  loop++)
  396.    if (!strcmp(s,elements[loop])) return loop;  //found it
  397. //did not find it-- add it
  398. (*this)[loop]= strdup(s);
  399. return loop;
  400. }
  401.  
  402. eltype& asa::operator[] (int index)
  403. {
  404. if (index >= capacity) {  //out of bounds
  405.    eltype* new_array= new eltype[index+1];
  406.    for (int loop= 0;  loop < index;  loop++)
  407.       new_array[loop]= elements[loop];
  408.    delete[capacity] elements;
  409.    elements= new_array;
  410.    capacity= index+1;
  411.    }
  412. return elements[index];
  413. }
  414.  
  415.  
  416. main()
  417. {
  418. asa a;
  419. for (;;) {
  420.    char c;
  421.    int index;
  422.    char value[40];
  423.    cout << "operation (r,w,b,q)? ";
  424.    cin >> c;
  425.    switch (c) {
  426.       case 'q':  goto done;
  427.       case 'b':  // 'backwards' read test
  428.          cout << "enter string ";
  429.          cin >> value;
  430.          cout << "found at " << a[value];
  431.          break;
  432.       case 'r': //read test
  433.          cout << "  index ";
  434.          cin >> index;
  435.          if (index < 0 || index >= a.size())
  436.             cout << "index out of range.";
  437.          else cout << "contains " << a[index];
  438.          break;
  439.       case 'w': //write test
  440.          cout << "  index and value ";
  441.          cin >> index >> value;
  442.          a[index]= strdup(value);
  443.          break;
  444.       case 's': //size?
  445.          cout << "array holds " << a.size() << " elements.";
  446.          break;
  447.       case 'l': //list them
  448.          for (index= 0;  index < a.size();  index++)
  449.             cout << "\n [" << index << "] " << a[index];
  450.          break;
  451.       }
  452.    cout << '\n';
  453.    }
  454. done:
  455. cout << "program finished.\n";
  456. }
  457. (8U&a1LLISTING
  458.  
  459. This program was based heavily on the previous.  I started with
  460. the vector class.  A global search and replace changed the name
  461. from (8U(s1Svector(s0S to (8U(s1Sasa(s0S, and simply changing the typedef of eltype
  462. changed the type to operate on char*'s instead of ints.  Then I
  463. added the new function:  (8U(s1Sint asa::operator[] (eltype s)(s0S.  It
  464. simply searches the array for a matching string.
  465.  
  466. The test driver also required only minor changes.  Changing the
  467. definition of the variable (8U(s1Svalue(s0S to the new type also changed the
  468. behavior of all the input and output statements that use it.  All
  469. I had to do was add a new case to try the string look up.
  470.  
  471. Note that this program is not all that great.  The simple stream
  472. input does not let me enter strings with a space in them, the
  473. array is not initialized so listing it can cause problems, and it
  474. is up to the user of the class to free up pointers before they
  475. are overwritten.
  476.  
  477. (8U(s3Boperator()(s0B
  478.  
  479. The operator() is another strange one.  It is sometimes called
  480. the (8U(s1Sfunction-call operator(s0S.  Like operator[], it must be a
  481. member.  The way to call it is easier shown than explained.  Look
  482. how it is used:
  483.  
  484. (8U&a6LLISTING
  485. class C {
  486.    //stuff...
  487. public:
  488.    void operator() (int x);
  489.    };
  490.  
  491. C x;
  492. //call operator()
  493. x(5);
  494. x.operator()(5);  //same thing
  495. (8U&a1LLISTING
  496.  
  497. Here, (8U(s1Sx(s0S is a variable of type (8U(s1SC(s0S.  Using the name as if it were a
  498. function will call operator().  When defining operator(), you
  499. give a parameter list just line any other operator, so you have
  500. two sets of parenthesis in the definition.  When calling it, you
  501. just have the one set.  The operator() can be defined to have any
  502. number of arguments-- it is the only operator that can do this.
  503.  
  504. (8U&a6LLISTING
  505. void C::operator()();  //no args
  506. int C::operator() (char* s, int y);  //2 args
  507. (8U&a1LLISTING
  508.  
  509. So what is the point in having such an operator?  For one, it can
  510. be used in a manner similar to the operator[].  Here is a vector
  511. class that uses operator[] to access an element with range
  512. checking, and operator() to access an element without range
  513. checking.
  514.  
  515. (8U&a6LLISTING
  516. // example of using operator() on a vector similar to operator[]
  517.  
  518. typedef int eltype;
  519. class vector {
  520.    eltype* contents;
  521.    int first, last;  //bounds of the array
  522. public:
  523.    vector (int first, int last);
  524.    ~vector() { delete[last-first+1] contents; }
  525.    eltype& operator[] (int index);   //access with range checking
  526.    eltype& operator() (int index)    //access without range
  527. checking
  528.       { return contents[index-first]; }
  529.    };
  530.  
  531. vector::vector (int f, int l)
  532. {
  533. first= f;
  534. last= l;
  535. contents= new eltype[l-f+1];
  536. }
  537.  
  538. eltype& vector::operator[] (int index)
  539. {
  540. if (index < first || index > last) {
  541.    //deal with the error somehow.  In this case,
  542.    //I'll return a special internal value that can
  543.    //be written to without trashing memory.
  544.    static eltype dump;
  545.    return dump;
  546.    }
  547. return contents[index-first];
  548. }
  549. (8U&a1LLISTING
  550.  
  551. If (8U(s1Sa(s0S is of type vector, you could access (8U(s1Sa[5](s0S or (8U(s1Sa(5)(s0S which are
  552. similar in meaning.  Having both [] and () available gives two
  553. different ways to subscript a vector.
  554.  
  555. Another thing you can do with the operator() is to take advantage
  556. of its ability to have different numbers of arguments.  The
  557. operator[] can only take one argument, but you might have a
  558. matrix class that takes two subscripts.  You could use operator()
  559. to subscript the class instead.
  560.  
  561. (8U&a6LLISTING
  562. // example of using operator() instead of operator[]
  563. // so I can use two arguments.
  564.  
  565. #include <assert.h>
  566.  
  567. const int matsize= 3;
  568. typedef double eltype;
  569. class square {
  570.    eltype data[matsize][matsize];
  571. public:
  572.    eltype& operator() (int x, int y);
  573.    };
  574.  
  575. eltype& square::operator() (int x, int y)
  576. {
  577. assert (x > 0 && y > 0 && x < matsize && y < matsize);
  578. return data[x][y];
  579. }
  580. (8U&a1LLISTING
  581.  
  582. Given a variable (8U(s1SM(s0S of type (8U(s1Ssquare(s0S, you could write (8U(s1SM(1,2)(s0S to
  583. access an element.
  584.  
  585. In the class definition, the eltype definition is used as usual.
  586. In this example, the size of the matrix is specified with (8U(s1Smatsize(s0S
  587. as well.  To change the size, you only need to change this
  588. definition.  All other parts of the code reference the size by
  589. this name.  In C, you would have to use a #define for this
  590. purpose.  In C++, a const variable can be used in constant
  591. expressions, such as the size of an array definition.
  592.  
  593. Another variation on this theme is a string class which uses
  594. operator() to take a substring.  Writing (8U(s1Ss(3,7)(s0S would return the
  595. third through seventh characters in the string s.
  596.  
  597. Another time operator() is used in when a class only has one
  598. method, or one very important method.  Consider an iterator
  599. class.  It steps through a linked list, returning the next
  600. element each time the (8U(s1Snext()(s0S method is called.  Rather than
  601. calling it (8U(s1Snext()(s0S, this sometimes uses operator() for that
  602. purpose.
  603.  
  604. (8U&a6LLISTING
  605. // example of using operator() in an iterator
  606.  
  607. class node {
  608.    friend class iterator;  //grant class iterator access to my
  609. private data
  610.    node* next;
  611. public:
  612.    char* data;
  613.    void insert_after (node*); //insert a node after this node.
  614.    };
  615.  
  616. class iterator {
  617.    node* p;  //keep track of my position in the list
  618. public:
  619.    node* operator()();  //advance to the next position
  620.    iterator (node* n) { p= n; }
  621.    };
  622.  
  623. void node::insert_after (node* p)
  624. {     //insert p after this node
  625. p->next= next;
  626. next= p;
  627. }
  628.  
  629. node* iterator::operator()()
  630. {     //return the node and advance to the next node
  631. if (!p) return p;  //end of the line, don't advance
  632. node* temp= p;
  633. p= p->next;
  634. return temp;
  635. }
  636. (8U&a1LLISTING
  637.  
  638. (8U(s3Boperator->(s0B
  639. The operator-> is the strangest one of all.  It must be a member.
  640. How it is called, and what it does, takes some explaining.
  641.  
  642. The operator-> must be defined to return a pointer type or a
  643. class type that itself has an operator-> defined.  The call is
  644. made with an object on the left and a member name on the right,
  645. such as (8U(s1Sx->a(s0S, but the (8U(s1Sa(s0S is not an argument to the function.  The
  646. operator-> is applied to (8U(s1Sx(s0S, and then the result is used on the
  647. left side of -> again.  The (8U(s1Sa(s0S will be the name of a field, not an
  648. argument of any kind.
  649.  
  650. The example (8U(s1Sx->a(s0S is equivalent to (8U(s1S(x.operator->())->a(s0S.  The
  651. operator is "slipped in" to the member access.
  652.  
  653. Here is an example of using operator-> to implement a "smart
  654. pointer".
  655.  
  656. (8U&a6LLISTING
  657. //example of using operator-> to create a "smart pointer"
  658.  
  659. class C {
  660.    //members go here...
  661. public:
  662.    int x;  //a public data member
  663.    void dosomething();  //member function
  664.    };
  665.  
  666. class Cptr {
  667.    C* p;
  668. public:
  669.    Cptr() { p=0; }  //always initialized
  670.    Cptr& operator= (C* ptr) { p= ptr; return *this; };
  671.    C* operator->();
  672.    };
  673.  
  674. extern void error();  //report an error, somehow
  675.  
  676. C* Cptr::operator->()
  677. {
  678. if (!p) {  //oops!  NULL pointer
  679.    error();
  680.    }
  681. else return p;
  682. }
  683. (8U&a1LLISTING
  684.  
  685. The smart pointer class holds a pointer to a C, and has
  686. operator-> defined on it so it will return that pointer.  You
  687. could have:
  688.  
  689. (8U&a6LLISTING
  690. C x;
  691. Cptr p;
  692. p= &x;
  693. y= p->x;  //refer to element in C
  694. p->dosomething();  //even member functions
  695. (8U&a1LLISTING
  696.  
  697. The operator-> is a unary operator that returns a C*, and then
  698. the -> operation is redone with that return value on the left.
  699. So (8U(s1Sp->x(s0S is equivalent to (8U(s1S(p.operator->())->x;(s0S.  If that still
  700. confuses you, remember that the operator is simply a member
  701. function with a funny name.  Calling it (8U(s1Sfetch()(s0S would let you say
  702. (8U(s1S(p.fetch())->x;(s0S which is perhaps clearer.
  703.  
  704. (8U(s3Boperator&(s0B
  705.  
  706. The unary form operator& is unusual because it is already defined
  707. for all class type.  It normally takes the address of the object.
  708. You can redefine it to do anything you want.  Normally it is used
  709. to inform the system that an object is having its address taken.
  710. It is sometimes used in debug code to report to the programmer
  711. when objects have their address taken.
  712.  
  713. (8U&a6LLISTING
  714. someclass* someclass::operator& ()
  715. {
  716. report_on (this);  //tell the programmer
  717. return this;  //and do what I came for.
  718. }
  719. (8U&a1LLISTING
  720.  
  721. This operator can be defined as a member function as shown above,
  722. or as a non-member function.  You should watch out for pitfalls
  723. when using an operator&.  Namely, how do you take the address of
  724. an object if operator& is defined?  This suggests one use of
  725. operator& is to make a class where you cannot take the address of
  726. an object-- operator& is private or causes a run-time error to be
  727. printed.
  728.  
  729. In the example of operator-> a smart pointer was illustrated.
  730. The Cptr type had an operator= that let you assign a C* to a
  731. Cptr.  Instead, you could use operator& to make you use smart
  732. pointers exclusively: define a (8U(s1SCptr C::operator&();(s0S so that
  733. taking the address of a C object gives a smart pointer directly.
  734.  
  735. (8U(s3Boperator,(s0B
  736.  
  737. The comma operator is not all that unusual.  It is rarely used
  738. because the comma is used so much in C++ already.  It is unusual
  739. in the respect that the comma is already defined between class
  740. types, so you have to be careful sometimes in knowing how
  741. overloading with resolve.
  742.  
  743. The comma operator forces left-to-right evaluation.  That can be
  744. handy.  The order of precedence is very low, so you will usually
  745. need parenthesis around the comma expression.
  746.  
  747.          (8U(s3Bcome up with an example(s0B
  748.  
  749. (8U(s3Boperator++ and operator--(s0B
  750.  
  751. These two are similar, and I'll just talk about operator++.  The
  752. same comments apply to operator--.
  753.  
  754. There are two forms of ++ for built-in types.  As a C programmer,
  755. you know that (8U(s1Sy= x++;(s0S and (8U(s1Sy= ++x(s0S; has different meanings.  In C++
  756. you can define both the prefix and postfix forms.
  757.  
  758. Defining the prefix form is exactly what you would expect, since
  759. it is the same as any other unary operator.  A member function
  760. such as (8U(s1SC::operator()(s0S  or a nonmember such as (8U(s1Soperator++(C&)(s0S
  761. will do.
  762.  
  763. The postfix form is defined with an extra argument.  This second
  764. argument is an int, and is always passed a value of 0.  Defining
  765. (8U(s1SC::operator++(int)(s0S  or (8U(s1Soperator++ (C&,int)(s0S will define a postfix
  766. operator.
  767.  
  768. Here is the smart pointer example again, showing prefix and
  769. postfix operators added.
  770.  
  771. (8U&a6LLISTING
  772. //example of using operator++ with the smart pointers
  773.  
  774. class C {
  775.    //members go here...
  776. public:
  777.    int x;  //a public data member
  778.    void dosomething();  //member function
  779.    };
  780.  
  781. class Cptr {
  782.    C* p;
  783. public:
  784.    Cptr() { p=0; }  //always initialized
  785.    Cptr& operator= (C* ptr) { p= ptr; return *this; };
  786.    C* operator->();  //see example in operator-> section
  787.    Cptr& operator++();  //preincrement
  788.    Cptr operator++(int);  //postincrement
  789.    };
  790.  
  791. Cptr& Cptr::operator++()
  792. {   //preincrement
  793. ++p;
  794. return *this;
  795. }
  796.  
  797. Cptr operator++ (int)
  798. {   //postincrement
  799. Cptr temp= *this;
  800. ++p;
  801. return temp;  //return original unmodified copy
  802. }
  803. (8U&a1LLISTING
  804.  
  805. (8U(s3BVersion Note(s0B
  806.  
  807. In C++ versions prior to 2.1, the postfix form was not available.
  808. The only way to define an operator++ or operator-- was with one
  809. argument.  It called this same function for either prefix or
  810. postfix use.  For compatibility, do not use such a function as a
  811. postfix operator.
  812. (8U(s3B